#include "StdAfx.h"
#include "GateServerRoot.h"
#include <xml.h>
#include <common.h>
#include <string>
#include <ut_debug.h>
#include "GateConnectorMgr.h"
#include "TimerMgr.h"
#include "SessionMgr.h"
#include "SubSessionMgr.h"
#include "MinuteTimer.h"
#include "SessionEventHandler.h"
#include "SubsessionEventHandler.h"
#include "DynLibManager.h"
#include "DynLib.h"
#include "Plugin.h"
#include "LogConfig.h"
#include "PipeManager.h"
#include "DBServer.h"
#include "ProxyMgr.h"
#include "SessionValidator.h"

using namespace std;
using namespace utility::network;
using namespace utility::xml;
using namespace utility::log;


GateServerRoot::GateServerRoot(void)
{
    m_pNetModule = NULL;
    m_pListener = NULL;
    m_pGateModule = NULL;
    m_pPipeModule = NULL;
    m_pProxyEventHandlerFactory = NULL;
}

GateServerRoot::~GateServerRoot(void)
{
    _DestroyInstance();
}

bool GateServerRoot::_Initialize()
{
    // initialize log system
    if (!_Init_log())
        return false;

    if (!m_pGateModule->initialize(m_pNetModule))
    {
        LOG(CRITICAL) << "Failed on initialize gate module";
        return false;
    }

    std::string strPipeConfigFile = utility::system::get_exe_path();
    strPipeConfigFile += "/GTPipeConfig.xml";
    if (!m_pPipeModule->initialize(strPipeConfigFile.c_str(), CPipeManager::Instance(), m_pNetModule))
    {
        LOG(CRITICAL) << "Failed on initializing pipe module";
        return false;
    }

    // load configure file
    std::string strConfigureFile = utility::system::get_exe_path();
    strConfigureFile += "/GateServerConfig.xml";
    if (!_LoadConfig(strConfigureFile.c_str()))
    {
        LOG(CRITICAL) << "Failed on loading configuration";
        return false;
    }

    CLogConfig oLogConfig;
    if (oLogConfig.Load("LogConfig.xml"))
    {
        LOG(CRITICAL) << "Failed on loading log configuration";
        return false;
    }

    LoadPlugin(m_strEventHandlerFileName);

    // start listen
    m_pListener = m_pNetModule->create_listener();
    m_pListener->set_session_mgr(CSessionMgr::Instance());
    if (!m_pListener->start(m_strIP.c_str() ,m_strPort.c_str()))
    {
        LOG(CRITICAL) << "Failed to listen IP:" << m_strIP << " ,port:" << m_strPort;
        return false;
    }

    // initialize the timer
    if (!CTimerMgr::Instance()->Init())
    {
        LOG(CRITICAL) << "Failed on initialization of timer manager.";
        return false;
    }

    LOG(INFO) << "Application successfully initialize.";

    return true;
}

bool GateServerRoot::_Init_log()
{
#ifdef _DEBUG
        utility::log::CDebugSinkConsole::Instance()->ShowHeader(true);
    #ifdef WIN32
        utility::log::CDebug::Instance()->SetSink(CDebugSinkWindows::Instance());
    #else
        utility::log::CDebug::Instance()->SetSink(CDebugSinkConsole::Instance());
    #endif

#else
    // File logger.
    #ifdef WIN32
        CDebugSinkFile::Instance()->SetFileDir(get_exe_path());
    #else
        CDebugSinkFile::Instance()->SetFileDir(".");
    #endif
        CDebugSinkFile::Instance()->ShowHeader(true);
        CDebugSinkFile::Instance()->SetFileHead("as_");
        CDebugSinkFile::Instance()->SetFileTail(".log");
        CDebug::Instance()->SetSink(CDebugSinkFile::Instance());
#endif

    CDebug::Instance()->TurnOnDebugType(CDebug::e_DefaultType);
    utility::log::CDebug::Instance()->OutputFileName(false);
    utility::log::CDebug::Instance()->OutputLineNumber(false);
    CDebug::Instance()->EnableAllDebugLevel();

    return true;
}

bool GateServerRoot::_LoadConfig( const char * szFile )
{
    CMarkupSTL xmlDoc;
    if (!xmlDoc.Load(szFile))
    {
        LOG(CRITICAL) << "Failed on open configuration file: " << szFile << " Error:" << xmlDoc.GetError();
        return false;
    }

    if (!xmlDoc.FindElem("root"))
    {
        LOG(CRITICAL) << "Failed to find XML node: root";
        return false;
    }
    xmlDoc.IntoElem();

    if (!xmlDoc.FindElem("network"))
    {
        LOG(CRITICAL) << "Failed to find XML node: network";
        return false;
    }

    m_strPort = xmlDoc.GetAttrib("listen_port");
    if (m_strPort.length() == 0)
    {
        LOG(CRITICAL) << "Failed to find XML attribution: network.listen_port";
        return false;
    }

    m_strIP = xmlDoc.GetAttrib("ip_address");
    if (m_strIP.length() == 0)
    {
        LOG(CRITICAL) << "Failed to find XML attribution: network.ip_address";
        return false;
    }

    if (!xmlDoc.FindElem("gate_connectors"))
    {
        LOG(CRITICAL) << "Failed to find XML node gate_connectors";
        return false;
    }

    xmlDoc.IntoElem();
    while (xmlDoc.FindElem("connector"))
    {
        string strListenerIP = xmlDoc.GetAttrib("ip");
        if (strListenerIP.empty())
        {
            LOG(CRITICAL) << "Failed to find XML attribution : gate\\connectors\\connector.ip";
            return false;
        }

        string strListenerPort = xmlDoc.GetAttrib("port");
        if (strListenerPort.empty())
        {
            LOG(CRITICAL) << "Failed to find XML attribution : gate\\connector\\connector.port";
            return false;
        }

        gate_connector * pGateConnector = m_pGateModule->create_connector();
        if (!pGateConnector->initialize(CSubSessionMgr::Instance()) || 
            !pGateConnector->connect_listener(strListenerIP.c_str(), strListenerPort.c_str()))
        {
            LOG(CRITICAL) << "Failed on initialization of gate connector";
            return false;
        }
        CGateConnectorMgr::Instance()->AddConnector(strListenerIP.c_str(), pGateConnector);

    }

    xmlDoc.OutOfElem();
    if (!xmlDoc.FindElem("eventhandler"))
    {
        LOG(CRITICAL) << "Failed to find XML node eventhandler";
        return false;
    }

    m_strEventHandlerFileName = xmlDoc.GetAttrib("file");
    if (m_strEventHandlerFileName.empty())
    {
        LOG(CRITICAL) << "Failed to find XML attribution : root\\eventhandler.file";
        return false;
    }

    return true;
}

bool GateServerRoot::_CreateInstance()
{
    if((m_pNetModule = get_net_module()) == NULL)
    {
        LOG(CRITICAL)<<"Failed on initialization of net module.";
        return false;
    }

    listener * pListener =  m_pNetModule->create_listener();
    if (pListener == NULL)
    {
        LOG(CRITICAL) << "Failed on initialization of network listener";
        return false;
    }

    if ((m_pGateModule = get_gate_module()) == NULL)
    {
        LOG(CRITICAL) << "Failed on initialization of gate module";
        return false;
    }

    if ((m_pPipeModule = get_pipe_module()) == NULL)
    {
        LOG(CRITICAL) << "Failed on initialization of pipe module";
        return false;
    }

    return true;
}

void GateServerRoot::_DestroyInstance()
{
    CSessionValidator::DestroyInstance();
    CDynLibManager::DestroyInstance();
    CProxyMgr::DestroyInstance();
    CPipeManager::DestroyInstance();
    CSubSessionMgr::DestroyInstance();
    CTimerMgr::DestroyInstance();
    CSessionMgr::DestroyInstance();
    CGateConnectorMgr::DestroyInstance();
    CMinuteTimer::DestroyInstance();
    destroy_gate_module();
    destroy_pipe_module();
    destroy_net_module();
    m_pNetModule = NULL;
    m_pListener = NULL;
    m_pGateModule = NULL;
    m_pPipeModule = NULL;
}

bool GateServerRoot::RunOnce()
{
    bool bBusy = false;
    if (m_pNetModule->run() > 0)
        bBusy = true;

    if (CTimerMgr::Instance()->Run())
        bBusy = true;
    return bBusy;
}

bool GateServerRoot::Init()
{
    return _CreateInstance() && _Initialize();
}
typedef void (*DLL_START_PLUGIN)(void);
typedef void (*DLL_STOP_PLUGIN)(void);
bool GateServerRoot::LoadPlugin( const std::string & strPluginName )
{
    // Load plugin library
    CDynLib* lib = CDynLibManager::Instance()->Load( strPluginName );
    // Store for later unload
    // Check for existence, because if called 2+ times DynLibManager returns existing entry
    if (std::find(m_PluginLibs.begin(), m_PluginLibs.end(), lib) == m_PluginLibs.end())
    {
        // Call startup function
        DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)lib->GetSymbol("dllStartPlugin");

        if (!pFunc)
        {
            LOG(CRITICAL) << "Cannot find symbol dllStartPlugin in library " << strPluginName << "Root::loadPlugin";
            return false;
        }

        m_PluginLibs.push_back(lib);
        // This must call installPlugin
        pFunc();
        return true;
    }
    return true;
}

void GateServerRoot::UnloadPlugin( const std::string & strPluginName )
{
    PluginLibList::iterator i;

    for (i = m_PluginLibs.begin(); i != m_PluginLibs.end(); ++i)
    {
        if ((*i)->GetName() == strPluginName)
        {
            // Call plugin shutdown
            DLL_STOP_PLUGIN pFunc = (DLL_STOP_PLUGIN)(*i)->GetSymbol("dllStopPlugin");
            // this must call uninstallPlugin
            pFunc();
            // Unload library (destroyed by DynLibManager)
            CDynLibManager::Instance()->Unload(*i);
            m_PluginLibs.erase(i);
            return;
        }

    }
}

void GateServerRoot::InstallPlugin( IPlugin* plugin )
{
    LOG(INFO) << "Installing plugin: " << plugin->GetName();
    m_Plugins.push_back(plugin);
    plugin->install();
    plugin->initialise();
}

void GateServerRoot::UninstallPlugin( IPlugin* plugin )
{
    LOG(INFO) << "Uninstalling plugin: " << plugin->GetName();
    PluginInstanceList::iterator i = 
        std::find(m_Plugins.begin(), m_Plugins.end(), plugin);
    if (i != m_Plugins.end())
    {
        plugin->shutdown();
        plugin->uninstall();
        m_Plugins.erase(i);
    }
}

void GateServerRoot::SetProxyEventHandlerFactory( IProxyEventHandlerFactory * pFactory )
{
    m_pProxyEventHandlerFactory = pFactory;
}

IProxyEventHandlerFactory * GateServerRoot::GetProxyEventHandlerFactory()
{
    return m_pProxyEventHandlerFactory;
}

utility::network::PIPEID GateServerRoot::GetDBServerPipeId()
{
    if (CDBServer::Instance())
    {
        return CDBServer::Instance()->GetPipeId();
    }
    utility::network::PIPEID pipeID={0};
    return pipeID;
}

uint16 GateServerRoot::GetListenerPort()
{
    return atoi(m_strPort.c_str());
}
